Skip to content

IoHandle interface#5829

Closed
danzh2010 wants to merge 14 commits intoenvoyproxy:masterfrom
danzh2010:iohandle
Closed

IoHandle interface#5829
danzh2010 wants to merge 14 commits intoenvoyproxy:masterfrom
danzh2010:iohandle

Conversation

@danzh2010
Copy link
Contributor

@danzh2010 danzh2010 commented Feb 4, 2019

Propose changes to IoHandle interface to wrap socket system calls. This PR is for discussion purpose, so that the IoHandle interface can be used as base class for both socket and non-socket implementations. It won't be checked in as the implementation for these interfaces will go in one by one in following PR's.
Part of #4546 #2557

Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
@danzh2010
Copy link
Contributor Author

/assign @venilnoronha, @mattklein123 , @sbelair2, @alyssawilk, @mpw, @jmarantz, @jrajahalme, @sesmith177, @ggreenway.

Add whoever commented on #5171. Feel free to unassign yourself if not interested any more.

@repokitteh-read-only
Copy link

🙀 Error while processing event:

evaluation error
error: function _rk_error error: path contains forbidden characters
🐱

Caused by: a #5829 (comment) was created by @danzh2010.

see: more, trace.

Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
@danzh2010
Copy link
Contributor Author

/assign @venilnoronha
/assign @mattklein123
/assign @sbelair2
/assign @alyssawilk
/assign @mpwarres
/assign @jmarantz
/assign @jrajahalme
/assign @sesmith177
/assign @ggreenway

@itayd
Copy link
Contributor

itayd commented Feb 4, 2019

@danzh1989 error is because of the commas. you can mention all users in the same "/assign" command, just no commas.

Signed-off-by: Dan Zhang <danzh@google.com>
* We probably still need some method similar to this one for
* evconnlistener_new(). Or We can move it to IoSocketHandle and down cast the
* IoHandle to IoSocketHandle wherever needed.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My opinion is that it would be cleaner to move this to the derived IoSocketHandle- what does the fd() method resolve to for interfaces that are not sockets? Either that, or there can be a generic method to manage a descriptor of sorts, such that for sockets, the descriptor is really just the fd. And for other interfaces it might be a session id, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer a generic method to return some sorts of identity which can be used differently for different implementation. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danzh1989 @sbelair2 I don't think I'm grokking this convo fully. Can one or both of you expand? I don't think we should still need this method so I'm likely missing something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattklein123 Maybe I missed some earlier conversation, but what shall be passed into evconnlistener_new() if there is no fd() or something similar in interface?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably figure out an interface strategy in which the socket actually creates the listener somehow, but for now, I would probably just do a dynamic_cast. It's a little ugly, but VPP or similar will have to have their own listener implementation, so it should fit together OK? Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattklein123 Do you mean to make fd() a method to IoSocketHandle only and dynamic_cast the IoHhandle object when we create listener? That sounds reasonable to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's what I meant. IMO that's OK for now.

virtual std::unique_ptr<IoHandle> dup() PURE;

virtual IoHandleCallIntResult shutdown(int how) PURE;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that there is probably no point in defining the template class IoHandleCallResult if all the virtual methods in the base class are all IoHandleCallResult or IoHandleCallResult<ssize_t>. I think we need to examine if there are any return values that can be other than integer types. If there are other interfaces that need this (I'm not saying that VPP is one of them), then won't these virtual methods preclude them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danzh2010 I do want to say that I like the direction where this is going.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you concerned about some implementations of same method whose return a type is different from what is declared here? For such method, I would say same purpose can be achieved by adding another new method to the interface.

If IoHandleCallResult and IoHandleCallResult<ssize_t> are the only two return types we need, what would you like the IoHandleCallResult to be without template?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danzh2010 Sorry, forgot about this. I'm only thinking that we should not preclude further options, but maybe that's just too much caution? Right now I cannot think of other return types we might need, but that doesn't mean that such a need won't come up later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we have IoHandleCallResult<std::bitset<2>>

Copy link
Contributor

@mpwarres mpwarres left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the perspective of what will work for QUIC, I think this should work fine, once sendmmsg/recvmmsg methods are added.

I feel like this interface has the potential to help with doing recvmsg from N sockets using SO_REUSEPORT, since it helps to hide the underlying FD (and thus may make it possible to have N FDs instead of just one), but I don't have a clear enough picture of this to suggest anything yet.

I think there's a top-level question to be answered (not by me) on whether the IoHandle API should be socket-style (like here) as opposed to more abstract. As an example, whether readv/writev should operate on Buffer::Instances rather than iovecs, or whether methods should indicate failure via exceptions rather than int error codes, etc. One argument in favor of the current form is that (a) it's a nice, step-wise change from previous code, and (b) now that all operations go through methods on IoHandle, they can always be changed to be higher-level in the future, incrementally.

/**
* Wrap getsockname()
*/
virtual IoHandleCallIntResult getIoHandleName(const Network::Address::Instance& address) PURE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: how will getIoHandleName() and getPeerName() actually return the name? They take const params, whereas getsockname() takes a mutable out-param. (Though making the Network::Address::Instance param non-const may not be sufficient, since Address::Instance isn't mutable in a way that allows it to be changed to a different address.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we will need some type of out param for these functions. You could either do an out param is an actual parameter, or have it return a struct of a result code and the address. I actually prefer the latter but I don't feel strongly about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer an output param of Network::Address::Instance type.

virtual IoHandleCallIntResult bind(const Network::Address::Instance& address) PURE;

virtual IoHandleCallIntResult connect(const Network::Address::Instance& server_address) PURE;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For UDP we will need methods that provide the equivalent of sendmmsg and recvmmsg.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. Not sure if envoy has something equivalent to mmsghdr or msghdr.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of any equivalent currently--the closest I can think of would be the UdpData struct @conqerAtapple added recently. Though if we wanted to use that, we would have to add analogues for msghdr.msg_control and msghdr.msg_flags fields. @conqerAtapple , do you have an opinion?

/**
* Wrap setsockopt()
*/
virtual IoHandleCallIntResult setIoHandleOption(int level, int optname, const void* optval,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest we use an enum for the options we care about. The one gotcha here is IIRC we have some capability in the config to actually set options that the code doesn't know about, by int, so maybe this isn't possible and we need to keep this? Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would there be potential crash if envoy fails to map the value set in config and the enum defined here? If so, introducing enum for socket option can be error-prone.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right I think if we have a config option to set an int, we need to leave it as is.

/**
* Wrap fcntl(fd_, F_SETFL...)
*/
virtual IoHandleCallIntResult setIoHandleFlag(int flag) PURE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum for options we care about?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added enum IoHandleFlag, but we only used O_NONBLOCK so far.

/**
* Wrap fcntl(fd_, F_GETFL...)
*/
virtual IoHandleCallIntResult getIoHandleFlag() PURE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return a bit set of enums that we care about?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean std::bitset or just int?

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks this is great to get this discussion going. I dropped in various comments.

Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
virtual IoHandleCallIntResult bind(const Network::Address::Instance& address) PURE;

virtual IoHandleCallIntResult connect(const Network::Address::Instance& server_address) PURE;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of any equivalent currently--the closest I can think of would be the UdpData struct @conqerAtapple added recently. Though if we wanted to use that, we would have to add analogues for msghdr.msg_control and msghdr.msg_flags fields. @conqerAtapple , do you have an opinion?

Signed-off-by: Dan Zhang <danzh@google.com>

namespace Envoy {

namespace Buffer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prefer include header vs. forward decl unless there is a circular dependency, same below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include/envoy/buffer/buffer.h will depend on io_handle.h, so will network/address.h.

class IoError {
public:
enum class IoErrorCode {
NoError = 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add comments for what each of these mean generically to Envoy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

IoHandle() {}
enum class ShutdownType { Read = 0, Write, Both };

enum class IoHandleFlag { NonBlock = 1, Append = 2 };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why explicitly number this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and ditto for the enums above (ShutdownType, IOError, ...).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Why explicitly number this?

This enum's values should be mapped to 2^n if we want it to fit into bitset.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, can you use 0x1, 0x10, etc. do make that clear?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you meant 0b1, 0b10. Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops sorry I meant 0x1, 0x2, 0x4, 0x10, etc. but either way.

/**
* Wrap fcntl(fd_, F_SETFL...)
*/
virtual IoHandleCallIntResult setIoHandleFlag(std::bitset<2> flag) PURE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document what the bitset actually means? Potentially with an enum of each bit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can just refer back to the enum which will have 0x1, etc. for docs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* We probably still need some method similar to this one for
* evconnlistener_new(). Or We can move it to IoSocketHandle and down cast the
* IoHandle to IoSocketHandle wherever needed.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably figure out an interface strategy in which the socket actually creates the listener somehow, but for now, I would probably just do a dynamic_cast. It's a little ugly, but VPP or similar will have to have their own listener implementation, so it should fit together OK? Thoughts?


class IoError {
public:
enum class IoErrorCode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this merges after #5772, can we also update that API to reference IOError here rather than do what it's doing?

Also IMO IOError should not be underneath the Network namespace, as this also makes sense for non-networked I/O.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR won't be checked in as a whole. Otherwise, all the implementations of these interfaces will have to be there and replacing all the direct sys call's to fd. I plan to check in the methods one by one in following PR's, starting from #5171 which will very likely has IoErrorCode definition.

Where would you like to place IoError?
And it will be great if #5772 can have TODO for switching to IoError if it's checked in first.

Signed-off-by: Dan Zhang <danzh@google.com>
Signed-off-by: Dan Zhang <danzh@google.com>
Copy link
Contributor Author

@danzh2010 danzh2010 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel we somewhat reached agreement on IoHandleCallResult and IoError now. If so, I would like to continue #5171 and copy them to that PR. Thoughts?

Signed-off-by: Dan Zhang <danzh@google.com>
@mattklein123
Copy link
Member

I feel we somewhat reached agreement on IoHandleCallResult and IoError now. If so, I would like to continue #5171 and copy them to that PR. Thoughts?

SGTM

@stale
Copy link

stale bot commented Feb 20, 2019

This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Feb 20, 2019
Signed-off-by: Dan Zhang <danzh@google.com>
@stale stale bot removed the stale stalebot believes this issue/PR has not been touched recently label Feb 21, 2019
@danzh2010
Copy link
Contributor Author

I updated IoError interface according to the discussion in #5171. This is to address memory allocation problem for frequent error like EAGAIN.

@stale
Copy link

stale bot commented Feb 28, 2019

This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Feb 28, 2019
@stale
Copy link

stale bot commented Mar 8, 2019

This pull request has been automatically closed because it has not had activity in the last 14 days. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot closed this Mar 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale stalebot believes this issue/PR has not been touched recently waiting

Projects

None yet

Development

Successfully merging this pull request may close these issues.